package eitwitter.learning;

import eitwitter.learning.models.ClassificationModel;
import eitwitter.normalization.Normalizer;
import eitwitter.storage.Category;
import eitwitter.storage.Company;
import eitwitter.storage.Tweet;
import eitwitter.storage.Vocabulary;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map.Entry;


/**
 * Classe se chargeant de l'apprentissage sur le corpus
 * @author AH et PJ
 */
public class Learning {
	
	/** Map liant les categories et leurs informations */
	private EnumMap<Category, CategoryInformations> categories;
	
	/** Methode de normalisation utilisee pour l'apprentissage */
	private Normalizer normalizer;
	
	/** Liste des mots vides */
	private ArrayList<String> stopwords;
	
	/** dictionnaire complet de tous les mots */
	private Vocabulary vocabulary;
	
	/** Modele de classification utilise pour l'apprentissage */
	private ClassificationModel classificationModel;
	
	/** Marque a traiter */
	private Company company = null;
	

	/**
	 * Constructeur de la classe
	 * lance l'apprentissage en utilisant le fichier passe en parametre
	 * @param file Fichier a traiter
	 */
	public Learning(Normalizer normalizer, ClassificationModel classificationModel){
		
		// Initialisation du conteneur de categories 
		this.categories = new EnumMap<Category, CategoryInformations>(Category.class);
		
		// Recuperation de la methode de normalisation voulue
		this.normalizer = normalizer;
		
		// Initialisation du dictionnaire des mots
		this.vocabulary = new Vocabulary();
		
		// Recuperation du modele de classification voulu
		this.classificationModel = classificationModel;
		
		// Initialisation de toutes les categories possibles
		for(Category category : Category.values()){
			this.categories.put(category, new CategoryInformations(this.normalizer, this.vocabulary, this));
		}
		
		// Initialisation de la liste des mots vides
		this.stopwords = new ArrayList<String>();
		
		this.company = null;
	}
	
	/**
	 * Constructeur de la classe
	 * lance l'apprentissage en utilisant le fichier passe en parametre
	 * @param file Fichier a traiter
	 */
	public Learning(Normalizer normalizer, ClassificationModel classificationModel, Company marque){
		
		// Initialisation du conteneur de categories 
		this.categories = new EnumMap<Category, CategoryInformations>(Category.class);
		
		// Initialisation de la marque à traiter
		this.company = marque;
		
		// Recuperation de la methode de normalisation voulue
		this.normalizer = normalizer;
		
		// Initialisation du dictionnaire des mots
		this.vocabulary = new Vocabulary();
		
		// Recuperation du modele de classification voulu
		this.classificationModel = classificationModel;
		
		// Initialisation de toutes les categories possibles
		for(Category category : Category.values()){
			this.categories.put(category, new CategoryInformations(this.normalizer, this.vocabulary, this));
		}
		
		// Initialisation de la liste des mots vides
		this.stopwords = new ArrayList<String>();
	}

	/**
	 * Ajoute les mots vides a la liste a partir du fichier passe en parametre
	 * @param file Fichier contenant la liste des mots vides
	 */
	public void addStopWordsFromFile(File file){
		try {
			// Ouverture du fichier
			InputStream ips = new FileInputStream(file);
			InputStreamReader ipsr = new InputStreamReader(ips);
			BufferedReader br = new BufferedReader(ipsr);
			
			// Traitement de la ligne en cours
			String line;
			while ((line=br.readLine())!=null){
				this.stopwords.add(line);
			}
			
			// Fermeture des pointeurs
			br.close();
			ips.close();
			
		} catch (IOException ex) {
			ex.printStackTrace();
		}
	}
	
	/**
	 * Traitement du fichier contenant les tweets d'apprentissage
	 * @param file Fichier a traiter
	 */
	public void learnFromFile(File file) {
		 
		// Recuperation des tweets depuis le fichier
		this.readTweetsFromFile(file);
		
		// Si des mots vides sont enregistres, on les supprime
		if(stopwords.size() > 0){
			this.removeStopwords();
		}
		
		for(Entry<Category, CategoryInformations> category : this.categories.entrySet()){
			category.getValue().makeStatistics();
		}
		
		// Lancement du calcul des betas
		this.computeBetas();
		
	}
	
	/**
	 * Lit le fichier contenant les tweets et les range par categorie
	 * @param file Fichier a traiter
	 */
	private void readTweetsFromFile(File file){
		
		try {
			// Ouverture du fichier
			InputStream ips = new FileInputStream(file);
			InputStreamReader ipsr = new InputStreamReader(ips);
			BufferedReader br = new BufferedReader(ipsr);
			
			// Traitement de la ligne en cours
			String line;
			while ((line=br.readLine())!=null){
				// Creation de l'objet Tweet pour la ligne lue
				Tweet t = Tweet.createFromString(line);
				if(this.company!=null){
					if(t.getCompany().equals(this.company))
						this.processTweet(t);
				}else{
					this.processTweet(t);
				}
			}
			
			// Fermeture des pointeurs
			br.close();
			ips.close();
			
		} catch (IOException ex) {
			ex.printStackTrace();
		}
	}

	/**
	 * Traitement d'un tweet du fichier
	 * @param Tweet Objet contenant un tweet et ses informations
	 */
	private void processTweet(Tweet t) {
		
		// Ajout du tweet a sa categorie
		this.categories.get(t.getCategory()).addTweet(t);
		
	}
	
	/**
	 * Retire pour chacune des categories les mots vides
	 */
	private void removeStopwords(){
		
		// On cree une liste avec les identifiants des mots vides
		ArrayList<Integer> stopWordsIds = new ArrayList<Integer>();
		
		// Recuperation des id dans le dictionnaire pour les mots vides
		for(String word : stopwords){
			int id = vocabulary.getId(word);
			if(id != -1){
				stopWordsIds.add(id);
				vocabulary.remove(id);
			}
		}
		
		// Pour chaque categorie, on demande la suppression des mots vides
		for(Category category : Category.values()){
			this.categories.get(category).removeWords(stopWordsIds);
		}
	}
	
	/**
	 * Retourne le nombre total d'occurences de toutes les categories
	 * @return Nombre total d'occurences de toutes les categories
	 */
	public int getTotalFrequencies(){
		int frequencies = 0;
		// Pour chaque categorie, on ajoute la frequence
		for(Category category : Category.values()){
			frequencies += this.categories.get(category).getWordsNumber();
		}	
		return frequencies;
	}
	
	/**
	 * Retourne la somme des frequences du mot passe en parametre pour toutes les categories
	 * @param word Mot dont on veut connaitre le nombre d'occurences totales
	 * @return Nombre total d'occurences du mot
	 */
	public int getTotalFrequencies(int word){
		int frequencies = 0;
		// Pour chaque categorie, on ajoute la frequence
		for(Category category : Category.values()){
			WordInformations wi = this.categories.get(category).getWords().get(word);
			if(wi != null){
				frequencies += wi.getFrequency();
			}
		}	
		return frequencies;
	}
	
	
	/**
	 * Lance le calcul des betas des mots avec le modele choisi
	 */
	private void computeBetas(){
		
		// Calcul de tous les betas des categories possibles
		for(Category category : Category.values()){
			classificationModel.computeBetas(this.categories.get(category));
		}
		
	}
	
	/**
	 * Lance le calcul de l'inference du tweet pour la categorie voulue
	 * @param tweet		Tweet dont on veut calculer l'inference
	 * @param category	Categorie par rapport a laquelle est fait le calcul
	 * @return Inference du tweet relativement a la categorie
	 */
	public double computeInference(Tweet tweet, Category category){
		return classificationModel.computeInference(tweet, categories.get(category));
	}
	
	public CategoryInformations getCategoryInformations(Category category){
		return this.categories.get(category);
	}

	public Vocabulary getVocabulary() {
		return vocabulary;
	}

	public Normalizer getNormalizer() {
		return normalizer;
	}	
	public Company getCompany() {
		return company;
	}

	public void setCompany(Company company) {
		this.company = company;
	}
	
}
